#pragma once

#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <sys/types.h>
#include <unistd.h>

enum
{
    DEBUG = 0,
    INFO,
    WARN,
    ERROR,
    FATAL
};

enum
{
    SCREEN = 0,
    SINGLE_FILE,
    MUTI_FILE
};

const int default_target = SCREEN;
const std::string default_filename = "log";

std::string levelToString(int level)
{
    switch (level)
    {
        case DEBUG: return "DEBUG";
        case INFO: return "INFO ";
        case WARN: return "WARN ";
        case ERROR: return "ERROR";
        case FATAL: return "FATAL";
        default: return "UNKNOW";
    }
}

class Log
{
public:
    Log(int target = default_target, std::string filename = default_filename + ".")
        : _target(target), _filename(filename)
    {}

    void message(int level, const char* format, ...)
    {
        char content[1024];
        va_list args; // char* args;
        va_start(args, format);
        vsnprintf(content, sizeof(content), format, args);
        va_end(args); // args = nullptr;

        char message[1024];
        snprintf(message, sizeof(message), "[%s][%s][%d] %s\n", 
            levelToString(level).c_str(), getLocalTime().c_str(), getpid(), content);

        writeLog(levelToString(level), message);
    }

    int getTarget()
    {
        return _target;
    }

    void setTarget(int target)
    {
        _target = target;
    }

    std::string getFilename()
    {
        return _filename;
    }

    void setFilename(std::string filename)
    {
        _filename = filename;
    }

    ~Log()
    {}
private:
    std::string getLocalTime()
    {
        time_t cur_time = time(nullptr);
        struct tm* tm = localtime(&cur_time);
        char time_buffer[128];
        snprintf(time_buffer, sizeof(time_buffer), "%d/%d/%d %d:%d.%d", 
            tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, 
            tm->tm_hour, tm->tm_min, tm->tm_sec);
        return time_buffer;
    }

    void writeLog(const std::string& level, const std::string& message)
    {
        switch (_target)
        {
        case SCREEN:
            std::cout << message;
            break;
        case SINGLE_FILE:
            writeLogToFile("ALL", message);
            break;
        case MUTI_FILE:
            writeLogToFile(level, message);
            break;
        default:
            break;
        }
    }

    void writeLogToFile(const std::string& level, const std::string& message)
    {
        std::string name = _filename + level;
        std::fstream fs;
        fs.open(name, std::ios::out|std::ios::app);
        fs << message;
        fs.close();
    }
private:
    int _target;
    std::string _filename;
};

Log log;

class LogConfig
{
public:
    LogConfig() 
    {
        log.setTarget(SCREEN);
    }
    ~LogConfig() {}
};

LogConfig log_config;